﻿using System;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using PublicTransport.Helpers;

namespace PublicTransport.Views.Controls
{
    /*
     * AutoComplete
     * 
     *   a text box which allows free-form entry of text
     *    whilst also providing auto-complete from a given
     *    list of strings
     * 
     *   an item can be selected from the list either by:
     *    a) choosing from the drop-down combobox list
     *    b) typing enough characters of the start of the 
     *        desired item to uniquely identify it
     * 
     * To use in a form:
     *  1) Drag onto a Form Designer using Visual Studio,
     *      using the Form Designer to resize/place the 
     *      box
     *  2) Use SetItems to provide the list of strings used
     *      to match for auto-complete
     *  3) Use AddItem to add any additional strings
     */
    public partial class AutoCompleteTextBox : UserControl
    {
        // stores the items that are used for auto-complete
        private object[] comboBoxItems = new object[0];

        public AutoCompleteTextBox()
        {
            InitializeComponent();

            // if the user uses the ComboBox to choose an item, 
            //  handle this by mirroring it in the TextBox
            innerComboBox.SelectedValueChanged += new EventHandler(innerComboBox_SelectedValueChanged);

            // if the user uses the TextBox to enter text, 
            //  handle this with our home-made autocomplete code below
            innerTextBox.KeyPress += new KeyPressEventHandler(innerTextBox_KeyPress);
        }

        // provide the array of strings to use for auto-complete matches
        // 
        // it will replace any previous list provided
        public void SetItems(object[] items)
        {
            comboBoxItems = items;

            Array.Sort(comboBoxItems);

            innerComboBox.Items.Clear();

            for (int i = 0; i < comboBoxItems.Length; i++)
            {
                innerComboBox.Items.Add(comboBoxItems[i]);
            }
        }
        // add an item to the array of strings used for auto-complete matches
        public void AddItem(string newitem)
        {
            string[] newitems = new string[comboBoxItems.Length + 1];

            Array.Copy(comboBoxItems, newitems, comboBoxItems.Length);

            newitems[comboBoxItems.Length] = newitem;

            SetItems(newitems);
        }
        // clear the list of strings used for auto-complete matches
        public void ClearItems()
        {
            comboBoxItems = new string[0];
            innerComboBox.Items.Clear();
        }

        // the user has used the ComboBox to select one of the possible 
        //  auto-complete matches
        //
        // handle this by mirroring it in the textbox
        void innerComboBox_SelectedValueChanged(object sender, EventArgs e)
        {
            innerTextBox.Text = innerComboBox.Text;
        }

        // the user has used the TextBox to modify the text
        //  
        // we look at what has been typed so far, and if it matches 
        //  anything in the list, we fill the text box with it
        // 
        // text which has been 'auto-complete-d' (rather than typed)
        //  is denoted by being selected
        void innerTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            // this is where we store any match found in the auto-complete list
            object match = null;

            // what has been typed so far?
            int cursorLocation = innerTextBox.SelectionStart;
            string typedSoFar = innerTextBox.Text.Substring(0, cursorLocation);

            // what is the user adding now?
            switch (e.KeyChar)
            {
                // BACKSPACE - the user is deleting a character - so we shink
                //   our 'typedSoFar' string by one character
                case (char)Keys.Back:
                    if (cursorLocation > 0)
                    {
                        cursorLocation -= 1;
                        typedSoFar = innerTextBox.Text.Substring(0, cursorLocation);
                    }
                    break;

                // DELETE - do nothing, allowing the 'delete' keystroke to delete
                //    the selected text provided from a previous auto-complete
                case (char)Keys.Delete:
                    break;

                // RETURN - do nothing - swallow this keystroke
                case (char)Keys.Return:
                    // don't do anything else
                    goto key_handle_complete;

                // OTHERWISE - assume we have a alphanumeric keystroke which 
                //   we add to the string typed-so-far
                default:
                    typedSoFar += e.KeyChar;
                    cursorLocation += 1;
                    break;
            }

            // look for a match in the auto-complete list
            for (int i = 0; i < comboBoxItems.Length; i++)
            {

                if (Helper.RemoveAccents(comboBoxItems[i].ToString()).StartsWith(Helper.RemoveAccents(typedSoFar), StringComparison.CurrentCultureIgnoreCase))
                {
                    match = comboBoxItems[i];

                    // we want first match - once found, break out
                    break;
                }
            }


            // was a match found?

            if (match == null)
            {
                // user has typed something not already in the list
                innerTextBox.Text = typedSoFar;
                innerTextBox.SelectionStart = typedSoFar.Length;
                innerTextBox.SelectionLength = 0;
            }
            else
            {
                // user has typed text which matches the start of something
                //  in the provided auto-complete list

                // display this match, and highlight the portion of it which
                //  was not actually typed by the user
                innerTextBox.Text = match.ToString();
                innerTextBox.SelectionStart = cursorLocation;
                innerTextBox.SelectionLength = innerTextBox.Text.Length - innerTextBox.SelectionStart;

                innerComboBox.SelectedItem = match;
            }

            // COMPLETE - finally, prevent key-press being handled by text box
        key_handle_complete: e.Handled = true;
        }

  
    



        public override string Text
        {
            get
            {
                String value=null;
                this.Invoke((System.Threading.ThreadStart)delegate
                {
                    value = innerTextBox.Text;
                });
                return value;
            }
            set
            {
                innerTextBox.Text = value;
            }
        }
    }
}